package eCheque;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Manager class for registering new accounts. All registration operations
 * should be done through this class.
 * 
 * @author Steve Pennington
 * 
 */
public class RegistrationManager implements ResponseHandler {

	private static RegistrationManager instance = new RegistrationManager();

	private List<RegistrationListener> listeners = new ArrayList<RegistrationListener>();
	private boolean waitingOnServer = false;
	private EChequeRegisteration account;

	public static RegistrationManager getInstance() {
		return instance;
	}

	/**
	 * Register an account with the EChequeServer and write the account
	 * information to disk
	 * 
	 * @param account
	 *            Account to register
	 * @param digitalCertificate
	 *            Digital certificate for the account's owner
	 * 
	 * @throws RegistrationInProcessException
	 *             If a registration is already in process
	 * @throws DuplicateAccountException
	 *             If an account with the same account number exists on the
	 *             filesystem
	 * @throws IOException
	 *             If an error occured wile accessing the filesystem or network
	 */
	public void registerAccount(EChequeRegisteration account,
			DigitalCertificate digitalCertificate)
			throws RegistrationInProcessException, DuplicateAccountException,
			IOException {
		synchronized (this) {
			if (waitingOnServer) {
				throw new RegistrationInProcessException();
			}
			this.waitingOnServer = true;
			this.account = account;
		}
		RegistrationUtils.writeAccount(account);
		Runnable client = new EchequeClient(EchequeClient.PORT_NUMBER,
				EchequeClient.ACTION_REGISTER, account.getBankAddress(),
				account, digitalCertificate, this);
		Thread t = new Thread(client);
		t.start();
	}
	
	public void deleteAccount(EChequeRegisteration account) throws RegistrationInProcessException {
		synchronized (this) {
			if (waitingOnServer) {
				throw new RegistrationInProcessException();
			}
			this.waitingOnServer = true;
			this.account = account;
		}
		Runnable client = new EchequeClient(EchequeClient.PORT_NUMBER, EchequeClient.ACTION_DELETE, 
				account);
		Thread t = new Thread(client);
		t.start();
	}

	/**
	 * Add a listener to be notified of registration events
	 * 
	 * @param listener
	 *            Listener to add
	 */
	public void addListener(RegistrationListener listener) {
		listeners.add(listener);
	}

	/**
	 * Stop a listener from being notified of registration events
	 * 
	 * @param listener
	 *            Listener to remove
	 */
	public void removeListener(RegistrationListener listener) {
		listeners.remove(listener);
	}

	private void fireAccountRegistered(EChequeRegisteration account) {
		for (RegistrationListener listener : listeners) {
			listener.accountRegistered(account);
		}
	}

	private void fireAccountFailed(EChequeResponse response,
			EChequeRegisteration account) {
		for (RegistrationListener listener : listeners) {
			listener.accountRegstrationFailed(response, account);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see eCheque.ResponseHandler#handleResponse(eCheque.EChequeResponse)
	 */
	@Override
	public void handleResponse(EChequeResponse response) {
		try {
			if (response.getResponseCode() == ResponseCode.OK) {
				fireAccountRegistered(account);
			} else {
				RegistrationUtils.deleteAccount(account);
				fireAccountFailed(response, this.account);
			}
		} finally {
			synchronized (this) {
				this.account = null;
				this.waitingOnServer = false;
			}
		}
	}
}
